Dir["#{RAILS_ROOT + '/app/classes/cat'}/*.rb"].each {|file| require file }

class CatController < ApplicationController

  #* Functionailities
  # * Creates a CAT Exam
  #* Parameters 
  # * HTTP POST
  #   * [uid] : user id
  #   * [section_id] : section id
  #* Return
  # * JSON
  #   * [theta] : initial user's ability
  #* Note
  # * N/A
  def create_exam
    uid = cookies[:uid]
    section_id = params[:section_id].to_i

    if section_id == 0
      _send_to_client(false, "Invalid Section")
      return
    end

    user = User.find_by_uid(uid)

    turn = 1
    theta = 0.0
    pre_exam = Exam.find(:first, :order=>'turn DESC')
    if pre_exam
      turn = pre_exam.turn + 1
    end
    
    pre_exam = Exam.find(:first, :conditions=>{:skill_id=>section_id}, :order=>'turn DESC')
    if pre_exam
      theta = pre_exam.theta
    end
    
    Exam.transaction do 
      exam = Exam.new
      exam.skill_id = section_id
      exam.user_id = user.id
      exam.exam_code = "#{user.id}_#{section_id}_#{turn}"
      exam.theta = theta
      exam.taken_at = Time.now
      exam.irt_model = CATModel::DICHOTOMOUS_2PL
      unless exam.save
        logger.info "ERROR : #{exam.errors.full_messages}"
        _send_to_client(false, 'System Error!!. Please contact system administrator (E-mail : mo@embian.com).')
        return
      end
    
      user.exam = exam
      unless user.save
        logger.info "ERROR : #{user.errors.full_messages}"
        _send_to_client(false, 'System Error!!. Please contact system administrator (E-mail : mo@embian.com).')
        return
      end
    end
    _send_to_client(true, 'Success', {:theta=>theta})
  end

  #* Functionailities
  # * Select an item for Starting Point or Next Item
  #* Parameters 
  # * HTTP POST
  #   * [uid] : user id
  #   * [theta] : user's ability
  #* Return
  # * JSON
  #   * [item] : JSON of item.attributes
  #   * [passage] : JSON of item.passage.attributes
  #   * [theta] : estimated user's ability
  #* Note
  # * N/A
  def select_item
    uid = cookies[:uid]
    theta = params[:theta]

    user = User.find_by_uid(uid)
    profile = _load_profile(user, theta.to_f)
    unless profile
      _send_to_client(false, "You ddon't start a TEST")
      return
    end
    
    cat_ruby = CATRuby.new(profile)
    cat_item = cat_ruby.select_item
    item = Item.find(:first, :select=>'id, passage_id, question, a, b, c, d', :conditions=>{:id=>cat_item.iid})
    
    _update_profile(profile)
    
    passage_attrs = item.passage.attributes if item.passage
    _send_to_client(true, "Success", {:item=>{:id=>item.id, 
                                              :question=>item.question, 
                                              :options=>[{:no=>'a', :option=>item.a}, {:no=>'b', :option=>item.b}, {:no=>'c', :option=>item.c}, {:no=>'d', :option=>item.d}]}, 
                                      :passage=>passage_attrs, 
                                      :theta=>1.0})
  end
  
  #* Functionailities
  # * Answer to item
  #* Parameters 
  # * HTTP POST
  #   * [uid] : user id
  #   * [item_id] : item id
  #   * [answer] : answer
  #   * [theta] : user's ability
  #* Return
  # * JSON
  #   * [theta] : estimated user's ability
  #   * [has_next_step] : true if needed to next item for estimating ability
  #* Note
  # * N/A
  def answer
    uid = cookies[:uid]
    item_id = params[:item_id]
    answer = params[:answer]
    theta = params[:theta]
    
    answer = nil if answer.to_s.strip.empty?

    unless item_id
      _send_to_client(false, 'System Error!!. Please contact system administrator (E-mail : mo@embian.com).')
      return
    end

    item = Item.find(item_id)
    unless item
      _send_to_client(false, 'System Error!!. Please contact system administrator (E-mail : mo@embian.com).')
      return
    end
    
    user = User.find_by_uid(uid)
    profile = _load_profile(user, theta.to_f)
    unless profile
      _send_to_client(false, "You ddon't start a TEST")
      return
    end
    
    cat_ruby = CATRuby.new(profile)
    cat_item = profile.exam.get_item(item.id)
    unless cat_item
      _send_to_client(false, 'System Error!!. Please contact system administrator (E-mail : mo@embian.com).')
      return
    end
    
    if answer
      part = ExamPart.find(:first, :conditions=>{:exam_id=>user.exam.id, :item_id=>cat_item.iid})
      unless part
        _send_to_client(false, 'System Error!!. Please contact system administrator (E-mail : mo@embian.com).')
        return
      end
    
      part.answer = answer
      unless part.save
        _send_to_client(false, 'System Error!!. Please contact system administrator (E-mail : mo@embian.com).')
        return
      end
    end
    
    #TODO : 시간 체크
    cat_ruby.response(cat_item, answer)
    cat_ruby.update_ability
    
    logger.info "#{profile.exam.examinee.responses.join(', ')}"
    
    _update_profile(profile)
    has_next_step = !(cat_ruby.can_stop)

    _send_to_client(true, "Success", {:has_next_step=>has_next_step, :theta=>profile.theta})
  end
  
  #* Functionailities
  # * Reports
  #* Parameters 
  # * HTTP POST
  #   * [uid] : user id
  #* Return
  # * JSON
  #   * [report] : TODO : makes report template
  #* Note
  # * N/A
  def get_reports
    uid = cookies[:uid]
    user = User.find_by_uid(uid)
    
    reports = []
    user.exams.each do |exam|
      parts = []
      exam.exam_parts.each do |part|
        parts << "&nbsp;&nbsp;- #{part.question_no} : discriments=[#{("%.3f" % part.item.irt_a.to_f)}], difficulty=[#{("%.3f" % part.item.irt_b.to_f)}], guessing=[#{("%.3f" % part.item.irt_c.to_f)}]"
      end
      str_report = "
                      <b>Taken At</b><br>
                       &nbsp;&nbsp;- #{exam.taken_at}<br><br />
                      <b>Section</b><br>
                       &nbsp;&nbsp;- #{exam.skill.name_en}<br><br />
                      <b>Number of Items</b><br />
                       &nbsp;&nbsp;- #{exam.exam_parts.size} units<br />
                       #{parts.join('<br>')}<br /><br />
                      <b>Estimated Ability</b><br />
                       &nbsp;&nbsp;- #{("%.5f" % exam.theta)}
                   "
      reports << {:report=>str_report}
    end
    
    _send_to_client(true, "Success", {:reports=>reports})
  end

  private
  #* Functionailities
  # * Load user profile
  #* Parameters
  # * [user] : an record of User
  #* Return
  # * [profile] : profile
  #* Note
  # * N/A
  def _load_profile(user, prev_theta)
    return @profile if @profile
    
    #Create UserProfile with Classification
    @profile = UserProfile.new(user.id, user.exam.skill.id)
    
    #Load IRT Model
    @profile.cat_model = CATModel.new(user.exam.irt_model)
        
    #Load Ability
    @profile.theta = user.exam.theta
    @profile.prev_theta = prev_theta
    
    #Load exam data
    user.exam.exam_parts.each do |part|
      cat_item = CATItem.new(part.item.id, part.item.irt_a.to_f, part.item.irt_b.to_f, part.item.irt_c.to_f)
      cat_item.answer = part.item.answer
      @profile.exam.add_item(cat_item)
      @profile.exam.answer_to(cat_item, part.answer)
    end
    
    return @profile
  end
  
  #* Functionailities
  # * Update user profile
  #* Parameters
  # * [user] : an record of User
  #* Return
  # * [profile] : profile
  #* Note
  # * N/A
  def _update_profile(profile)
    User.transaction do 
      user = User.find(profile.user_id)
      #Update Ability
      user.exam.theta = profile.theta
      
      #Update exam data
      cat_item = profile.exam.last_item
      part = ExamPart.find(:first, :conditions=>{:exam_id=>user.exam.id, :item_id=>cat_item.iid})
      is_new_part = false
      unless part
        part = ExamPart.new 
        is_new_part = true
      end
      
      part.exam_id = user.exam.id
      part.item_id = cat_item.iid
      part.question_no = cat_item.no

      unless part.save
        raise part.errors.full_messages.join("\n")
      end
      
      user.exam.exam_parts << part unless is_new_part
      

      unless user.exam.save
        raise user.exam.errors.full_messages.join("\n")
      end
    end
    
    return true
  end
end